#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "sans.h"
#include "stack.h"

#define MAX_STACK (1<<20)

u stack_ptr = 0;
sdata stack[MAX_STACK];

void stack_dump() {
  u i = stack_ptr;
  //puts("--- stack dump start ---");
  while(i > 0) {
    printf("%d\n", stack[i--].tag);
  }
  //puts("--- stack dump end ---");
}

void stack_push(sdata obj) {
  //printf("stack_push(%d)\n", obj.tag);
  if (stack_ptr < MAX_STACK) {
    stack[stack_ptr++] = obj;
  }
  else {
    log_err("stack/stack_push: overflow\n");
  }
}

sdata stack_pop() {
  //printf("stack_pop()\n");
  if (stack_ptr) {
    return stack[--stack_ptr];
  }
  else {
    log_err("stack/stack_pop: underflow\n");
  }
}

void stack_grow(u n) {
  //printf("stack_grow(%lu)\n", n);
  u new = stack_ptr + n;
  if (new <= MAX_STACK) {
    stack_ptr = new;
  }
  else {
    log_err("stack/stack_grow: overflow\n");
  }
}

void stack_shrink(u n) {
  //printf("stack_shrink(%lu)\n", n);
  u new = stack_ptr - n;
  if (new >= 0) {
    stack_ptr = new;
  }
  else {
    log_err("stack/stack_shrink: underflow\n");
  }
}

sdata stack_ref(u o) {
  u o1 = stack_ptr - o - 1;
  if (o1 >= 0) {
    return stack[o1];
  }
  else {
    log_err("stack/stack_ref: out of bounds\n");
  }
}

void stack_set(u o, sdata val) {
  u o1 = stack_ptr - o - 1;
  if (o1 >= 0) {
    stack[o1] = val;
  }
  else {
    log_err("stack/stack_set: out of bounds\n");
  }
}

void stack_fold(u width, u distance) {
  // stack_fold(2, 3) will move
  // the top 2 things on the stack
  // back down by 3
  //
  // [a][b][c][d][e][f]
  // [a][e][f]
  //

  memmove(&stack[stack_ptr-width-distance],
	  &stack[stack_ptr-width],
	  sizeof(sdata)*width);
  stack_ptr -= distance;
}
